#include <util.h>
#include <arch/arm_mpu.h>

#define ATTR_DEVICE_nGnRE_INDEX     (0)
#define ATTR_NORMAL_INDEX           (1)
#define ATTR_NORMAL_NONCACHE_INDEX  (2)
#define ATTR_DEVICE_nGnRnE_INDEX    (3)

#define DEVICE_REGION_START 0x80000000UL
#define DEVICE_REGION_END   0xFFFFFFFFUL

static const struct arm_mpu_region mpu_regions[] = {
    /* Region 0 */
    {
        .name = "RAM",
        .base = 0x0,
        .limit = 0x7fffffff,
        .attr = {
            .xn = 0,                         /* eXecute Never */
            .ap = P_RW_U_RW_Msk,             /* Access Permission: Read/write at EL1 and EL0 */
            .sh = OUTER_SHAREABLE_Msk,       /* share-ability: */
            .mair_idx = ATTR_NORMAL_INDEX,
        }
    },
    /* Region 1 */
    {
        .name = "DEVICE",
        .base = DEVICE_REGION_START,
        .limit = DEVICE_REGION_END,
        .attr = {
            .xn = 1,                         /* eXecute Never */
            .ap = P_RW_U_RW_Msk,             /* Access Permission: Read/write at EL1 and EL0 */
            .sh = OUTER_SHAREABLE_Msk,       /* share-ability: */
            .mair_idx = ATTR_DEVICE_nGnRE_INDEX,
        }
    }
};

const struct arm_mpu_config plat_mpu_config = {
    .num_regions = ARRAY_SIZE(mpu_regions),
    .mpu_regions = mpu_regions,
};

void plat_mpu_mair_config(void)
{
    /** AttrIndx[2] indicates the MAIR register to be used
     *  AttrIndx[2] == 0, Use MAIR0
     *  AttrIndx[2] == 1, Use MAIR1
     */
    uint32_t mair0;

    /* 0 -> Device-nGnRE */
    mair0 = MAIR_ATTR_SET(MAIR_DEV_nGnRE, ATTR_DEVICE_nGnRE_INDEX);

    /* 1 -> Normal Memory, Outer Write-Back non-transient, Inner Write-Back non-transient, R/W allocate */
    mair0 |= MAIR_ATTR_SET(MAKE_MAIR_NORMAL_MEMORY(MAIR_NORM_WB_NTR_RWA, MAIR_NORM_WB_NTR_RWA), ATTR_NORMAL_INDEX);

    /* 2 -> Normal Memory, Outer Write-Through non-transient, Inner Non-cacheable */
    mair0 |= MAIR_ATTR_SET(MAKE_MAIR_NORMAL_MEMORY(MAIR_NORM_NC, MAIR_NORM_NC), ATTR_NORMAL_NONCACHE_INDEX);

    /* 3 -> Device-nGnRnE */
    mair0 = MAIR_ATTR_SET(MAIR_DEV_nGnRnE, ATTR_DEVICE_nGnRnE_INDEX);

    write_mair0(mair0);
}

